package cbbx_sm.utils.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import cbbx_sm.parser.BoundingBox;
import cbbx_sm.parser.Entity;
import cbbx_sm.parser.Frame;
import cbbx_sm.probabilistic_model.Cluster;
import cbbx_sm.probabilistic_model.Prediction;
import cbbx_sm.utils.ExperimentManager;
import cbbx_sm.utils.Timestamp;


public class FramePanel extends JPanel {

	private static final long serialVersionUID = 1L;
	
	private static final int clusterCenterCrossSize = 3;
	
	private float height;
	private float width;
	public static int imageHeight = 480;
    public static int imageWidth = 708;
    private float x_proportion, y_proportion;
    
    private RescaleOp rop;
    
	private Frame curFrame = null;
	private List<Cluster> clusters;

	private Cluster prediction = null;
	private Cluster actualCluster = null;
	private int hits = 0;
	private int misses = 0;
	
	private Timestamp timestamp = null;
	private String imageFileBackground; 
	private String imageFolder;

	private Prediction curPrediction = null;
	
 	private BufferedImage backgroundImage = null;

		
	public FramePanel(List<Cluster> clusters, String imageFile, String imageFolder, 
			float width, float height){
		super();
		this.height = height;
		this.width = width;

		this.clusters = clusters;
		
		// Calculating maxWeight and maxHeight in base of maximum
		// coordinate of bounding boxes.
//	    for (BoundingBox b : boxes){
//	    	if (maxHeight < b.getY_ul()) maxHeight = b.getY_ul();
//	    	if (maxHeight < b.getY_br()) maxHeight = b.getY_br();
//	    	if (maxWidth < b.getX_br()) maxWidth = b.getX_br();
//	    	if (maxWidth < b.getX_ul()) maxWidth = b.getX_ul();
//	    }
	    
//	    x_proportion = width / maxWidth;
//	    y_proportion = height / maxHeight;
	    x_proportion = (width - 10) / imageWidth;
	    y_proportion = (height - 30) / imageHeight;
	    
	    this.imageFileBackground = imageFile;
	    this.imageFolder = imageFolder;
	    this.backgroundImage = updateBackgroundImage(imageFileBackground);
	}
	
	private BufferedImage updateBackgroundImage(String imageFile) {
	    // Creating background image: loading, scaling and processing...
		BufferedImage background = null;
	    BufferedImage original = null;
	    try {
	    	original = ImageIO.read(new File(imageFile));
	    } catch (IOException e) {
			e.printStackTrace();
			System.err.println(imageFile);
			return null;
		}
		
	    Image scaledImage = original.getScaledInstance((int)width - 5, (int)height - 25, Image.SCALE_FAST);
		BufferedImage temp = new BufferedImage((int) width - 5, (int) height - 25, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = (Graphics2D) temp.getGraphics();
		g.drawImage(scaledImage, 0, 0, null);
		
		/* Create a rescale filter op that makes the image 50% opaque */
	    float[] scales = { 1f, 1f, 1f, 1f };//{ 1f, 1f, 1f, 0.7f };
	    float[] offsets = new float[4];
	    rop = new RescaleOp(scales, offsets, null);
	    
	    background = new BufferedImage((int) width, (int) height, BufferedImage.TYPE_INT_ARGB);
	    g = (Graphics2D) background.getGraphics();
	    g.drawImage(temp, rop, 0, 0);
	    
	    return background;
	    
	}

	public void paint(Graphics g) {
	    
	    Graphics2D g2 = (Graphics2D)g;
	    g2.clearRect(0, 0, (int)width, (int)height);
	    
	    if (actualCluster == null){
		    // Painting the background
		    g2.drawImage(backgroundImage, 0, 0, null);
	    } else {
	    	BufferedImage background = updateBackgroundImage(imageFolder + "/" + curFrame.getCameraId().replace("cam", "")
		    		+ "/" + curFrame.getCameraId().replace("cam", "")+"_"+curFrame.getTimestamp()+".jpg");
		    
		    // Painting the background
		    g2.drawImage(background, 0, 0, null);
		    
		    for (Entity e : curFrame.getEntities()){
		    	BoundingBox b = e.getBoxes()[ExperimentManager.boxLocationId];
		    	
	    		float x_br = b.getX_br() * x_proportion;
	    		float x_ul = b.getX_ul() * x_proportion;
	    		float y_br = b.getY_br() * y_proportion;
	    		float y_ul = b.getY_ul() * y_proportion;
	    		
	    		
	    		Rectangle2D rectangle = new Rectangle2D.Float(x_ul, y_ul, 
	    													  x_br - x_ul, y_br - y_ul);    		
	
	    		g2.draw(rectangle);
	    		
	    		// Drawing bounding box center.
	    		Point2D point1 = new Point2D.Double(rectangle.getCenterX() + clusterCenterCrossSize, rectangle.getCenterY() - clusterCenterCrossSize);
	    		Point2D point2 = new Point2D.Double(rectangle.getCenterX() - clusterCenterCrossSize, rectangle.getCenterY() + clusterCenterCrossSize);
	    		g2.draw(new Line2D.Double(point1, point2));
	    		
	    		point1 = new Point2D.Double(rectangle.getCenterX() + clusterCenterCrossSize, rectangle.getCenterY() + clusterCenterCrossSize);
	    		point2 = new Point2D.Double(rectangle.getCenterX() - clusterCenterCrossSize, rectangle.getCenterY() - clusterCenterCrossSize);
	    		g2.draw(new Line2D.Double(point1, point2));
	    	}
	    }

    	if (clusters == null) return;
    	   	
    	for(Cluster c : clusters){
    		double clusterx = c.getX() * x_proportion;
    		double clustery = c.getY() * y_proportion;
    		double radiusx = c.getRadius() * x_proportion;
    		double radiusy = c.getRadius() * y_proportion;
    		Ellipse2D.Double clusterShape = new Ellipse2D.Double(clusterx - radiusx, clustery - radiusy, 2.0 * radiusx, 2.0 * radiusy);
    		
    		// Painting the center of the cluster
        	g2.setColor(Color.RED);
        	g2.setStroke(new BasicStroke(1.5f));
        	
    		Point2D.Double point1 = new Point2D.Double(clusterx - clusterCenterCrossSize, clustery - clusterCenterCrossSize);
    		Point2D.Double point2 = new Point2D.Double(clusterx + clusterCenterCrossSize, clustery + clusterCenterCrossSize);
    		g2.draw(new Line2D.Double(point1, point2));
    		
    		point1 = new Point2D.Double(clusterx + clusterCenterCrossSize, clustery - clusterCenterCrossSize);
    		point2 = new Point2D.Double(clusterx - clusterCenterCrossSize, clustery + clusterCenterCrossSize);
    		g2.draw(new Line2D.Double(point1, point2));
    		   		
    		// Painting the shape of the cluster
    		g2.setColor(Color.ORANGE);
        	g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {15.0f, 6.0f, 2.5f, 6.0f}, 0.0f));

    		// Make everything white except for the cluster that we zoomed into.
    		if (c == actualCluster) {
    			g2.setColor(Color.GREEN);
    		}
    		if (c == prediction) {
    			g2.setColor(Color.WHITE);
    			Area allButRegion = new Area(new Rectangle((int) width, (int) height));
    			Area clusterArea = new Area(clusterShape);
    			allButRegion.subtract(clusterArea);
    			g2.fill(allButRegion);
    			
    			// Bullseye
    			if (c == actualCluster) {
        			g2.setColor(Color.GREEN);
                	g2.setStroke(new BasicStroke(25f));
        			try {
        				Thread.sleep(1000);
        			} catch (Exception e) {}
    			} else {
        			g2.setColor(Color.RED);
                	g2.setStroke(new BasicStroke(10f));
    			}
    		}
    		if (curPrediction!=null) {
        		g2.drawString(
        				String.format("p=%.4f", 
        						curPrediction.getClusterProbabilities().get(c)), (int)point1.x - clusterCenterCrossSize, (int)point1.y - clusterCenterCrossSize);    			
    		}  	
        	g2.draw(clusterShape);
    	}
    	
    	g2.setColor(Color.BLACK);
    	g2.drawString("HITS: " + hits + "; MISSES: " + misses, 20, 30);
    	g2.drawString("TIMESTAMP: " + timestamp, 20, 45);
	}
	
	public Frame getCurFrame() {
		return curFrame;
	}

	public void setCurFrame(Frame curFrame) {
		this.curFrame = curFrame;
	}

	public void setPrediction(Cluster prediction) {
		this.prediction = prediction;
	}

	public void setHits(int hits) {
		this.hits = hits;
	}

	public int getHits() {
		return hits;
	}

	public void setMisses(int misses) {
		this.misses = misses;
	}

	public int getMisses() {
		return misses;
	}
	
	public List<Cluster> getClusters() {
		return clusters;
	}

	public void setClusters(List<Cluster> clusters) {
		this.clusters = clusters;
	}
	
	public void setPrediction(Prediction curPrediction) {
		this.curPrediction  = curPrediction;
	}

	public void setActualCluster(Cluster actualCluster) {
		this.actualCluster = actualCluster;
	}

	public Cluster getActualCluster() {
		return actualCluster;
	}

	public void setTimestamp(Timestamp timestamp) {
		this.timestamp = timestamp;
	}

	public Timestamp getTimestamp() {
		return timestamp;
	}

}

